import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
import 'rotate_scale_gesture_recognizer.dart';

import 'hit_corners.dart';

class CutCanvasGestureDetector extends StatelessWidget {
  const CutCanvasGestureDetector({
    Key key,
    @required this.child,
    this.hitDetector,
    this.onScaleStart,
    this.onScaleUpdate,
    this.onScaleEnd,
    this.onTapUp,
    this.onTapDown,
  }) : super(key: key);

  final HitCornersDetector hitDetector;

  final GestureRotateScaleStartCallback onScaleStart;
  final GestureRotateScaleUpdateCallback onScaleUpdate;
  final GestureRotateScaleEndCallback onScaleEnd;

  final GestureTapUpCallback onTapUp;
  final GestureTapDownCallback onTapDown;

  final Widget child;

  @override
  Widget build(BuildContext context) {
    final scope = ConflictResolveGestureDetectorScope.of(context);
    final Axis axis = scope?.axis;

    final Map<Type, GestureRecognizerFactory> gestures =
        <Type, GestureRecognizerFactory>{};
    if (onTapDown != null || onTapUp != null) {
      gestures[TapGestureRecognizer] =
          GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
        () => TapGestureRecognizer(debugOwner: this),
        (TapGestureRecognizer instance) {
          instance
            ..onTapDown = onTapDown
            ..onTapUp = onTapUp;
        },
      );
    }
    gestures[ConflictResolveGestureRecognizer] =
        GestureRecognizerFactoryWithHandlers<ConflictResolveGestureRecognizer>(
      () => ConflictResolveGestureRecognizer(
          hitDetector: hitDetector, debugOwner: this, validateAxis: axis),
      (ConflictResolveGestureRecognizer instance) {
        instance
          ..onStart = onScaleStart
          ..onUpdate = onScaleUpdate
          ..onEnd = onScaleEnd;
      },
    );

    /// 如果需要新增其他手势处理，可以在这里添加

    return RawGestureDetector(
      behavior: HitTestBehavior.translucent,
      child: child,
      gestures: gestures,
    );
  }
}

/// 解决手势冲突的问题，同时处理双指缩放旋转等手势
class ConflictResolveGestureRecognizer extends RotateScaleGestureRecognizer {
  ConflictResolveGestureRecognizer({
    this.hitDetector,
    this.validateAxis,
    Object debugOwner,
    PointerDeviceKind kind,
  }) : super(debugOwner: debugOwner, kind: kind);
  final HitCornersDetector hitDetector;
  final Axis validateAxis;

  Map<int, Offset> _pointerLocations = <int, Offset>{};

  Offset _initialFocalPoint;
  Offset _currentFocalPoint;

  bool ready = true;

  @override
  void addAllowedPointer(PointerEvent event) {
    if (ready) {
      ready = false;
      _pointerLocations = <int, Offset>{};
    }
    super.addAllowedPointer(event);
  }

  @override
  void didStopTrackingLastPointer(int pointer) {
    ready = true;
    super.didStopTrackingLastPointer(pointer);
  }

  @override
  void handleEvent(PointerEvent event) {
    if (validateAxis != null) {
      _computeEvent(event);
      _updateDistances();
      _decideIfWeAcceptEvent(event);
    }
    super.handleEvent(event);
  }

  void _computeEvent(PointerEvent event) {
    if (event is PointerMoveEvent) {
      if (!event.synthesized) {
        _pointerLocations[event.pointer] = event.position;
      }
    } else if (event is PointerDownEvent) {
      _pointerLocations[event.pointer] = event.position;
    } else if (event is PointerUpEvent || event is PointerCancelEvent) {
      _pointerLocations.remove(event.pointer);
    }

    _initialFocalPoint = _currentFocalPoint;
  }

  void _updateDistances() {
    final int count = _pointerLocations.keys.length;
    Offset focalPoint = Offset.zero;
    for (int pointer in _pointerLocations.keys)
      focalPoint += _pointerLocations[pointer];
    _currentFocalPoint =
        count > 0 ? focalPoint / count.toDouble() : Offset.zero;
  }

  void _decideIfWeAcceptEvent(PointerEvent event) {
    if (!(event is PointerMoveEvent)) {
      return;
    }
    final move = _initialFocalPoint - _currentFocalPoint;
    final bool shouldMove = validateAxis == Axis.vertical
        ? hitDetector.shouldMoveY(move)
        : hitDetector.shouldMoveX(move);
    if (shouldMove || _pointerLocations.keys.length > 1) {
      acceptGesture(event.pointer);
    }
  }
}

/// 为了解决父控件手势冲突的情况，当缩放到边缘状态时，会由父控件获取手势事件
/// 例如[PageView], [Dismissible], [BottomSheet]中，出现手势冲突时，这会非常有用
class ConflictResolveGestureDetectorScope extends InheritedWidget {
  ConflictResolveGestureDetectorScope({
    this.axis,
    @required Widget child,
  }) : super(child: child);

  static ConflictResolveGestureDetectorScope of(BuildContext context) {
    final ConflictResolveGestureDetectorScope scope =
        context.dependOnInheritedWidgetOfExactType<
            ConflictResolveGestureDetectorScope>();
    return scope;
  }

  final Axis axis;

  @override
  bool updateShouldNotify(ConflictResolveGestureDetectorScope oldWidget) {
    return true;
  }
}
